/*
 *  Copyright (C) 2003,14 R. De Graeve & B. Parisse,
 *  Institut Fourier, 38402 St Martin d'Heres
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "dimension.h"

CASPP_NS_BEGIN

boost::container::vector<Dimension> Dimension::cachedDimensions_s;

Dimension::Dimension(Unit mass,
          Unit position,
          Unit time,
          Unit charge,
          Unit temperature,
          Unit intensity,
          Unit angle,
          Unit other)
    : __super() {
    Us_m[0] = mass;
    Us_m[1] = position;
    Us_m[2] = time;
    Us_m[3] = charge;
    Us_m[4] = temperature;
    Us_m[5] = intensity;
    Us_m[6] = angle;
    Us_m[7] = other;
}

Dimension::Dimension()
    : __super() {
    for (int i = 0; i<Size;++i) {
        Us_m[i]=0;
    }
}

Dimension::Dimension(const Dimension & __x)
    : __super() {
    for (int i = 0; i<Size;++i) {
        Us_m[i]=__x.Us_m[i];
    }
}

const Dimension Dimension::operator *(const Dimension & __x) {
    Dimension out(*this);
    for (int i = 0; i<Size; ++i) {
        // Expected: Min < Us_m[i]+__x.Us_m[i] < Max
        if (Us_m[i]>Min) {
            if (Us_m[i]<0) /* Expected: Min - Us_m[i]< __x.Us_m[i] < Max */ {
                if (__x.Us_m[i]<Max) {
                    if (Min - Us_m[i]< __x.Us_m[i]) {
                        out.Us_m[i] = Us_m[i]+__x.Us_m[i];
                    } else {
                        out.Us_m[i] = Min;
                    }
                } else {
                    out.Us_m[i] = Max;
                }
            } else /* Expected: Min < __x.Us_m[i] < Max - Us_m[i] */ if (Min < __x.Us_m[i]) {
                if (__x.Us_m[i] < Max - Us_m[i]) {
                    out.Us_m[i] = Us_m[i]+__x.Us_m[i];
                } else {
                    out.Us_m[i] = Max;
                }
            } else {
                out.Us_m[i] = Min;
            }
        } else {
            out.Us_m[i] = Min;
        }
    }
    return out;
}

const Dimension Dimension::operator /(const Dimension & __x) {
    Dimension out(*this);
    for (int i = 0; i<Size; ++i) {
        // Expected: Min < Us_m[i]-__x.Us_m[i] < Max
        if (Us_m[i]>Min) {
            if (Us_m[i]<0) /* Expected: Min - Us_m[i]< -__x.Us_m[i] < Max */ {
                if (-__x.Us_m[i]<Max) {
                    if (Min - Us_m[i]< -__x.Us_m[i]) {
                        out.Us_m[i] = Us_m[i]-__x.Us_m[i];
                    } else {
                        out.Us_m[i] = Min;
                    }
                } else {
                    out.Us_m[i] = Max;
                }
            } else /* Expected: Min < -__x.Us_m[i] < Max - Us_m[i] */ if (Min < -__x.Us_m[i]) {
                if (-__x.Us_m[i] < Max - Us_m[i]) {
                    out.Us_m[i] = Us_m[i]-__x.Us_m[i];
                } else {
                    out.Us_m[i] = Max;
                }
            } else {
                out.Us_m[i] = Min;
            }
        } else {
            out.Us_m[i] = Min;
        }
    }
    return out;
}

bool Dimension::operator == (const Dimension & __x) {
    return memcmp(Us_m,__x.Us_m,sizeof(Us_m)) == 0;
}

Dimension::Index Dimension::index() {
    Dimension::Index i = 0;
    for (i=0; i<cachedDimensions_s.size();++i) {
        if (cachedDimensions_s[i]==*this) {
            return i;
        }
    }
    if (++i<IndexMax) {//cachedDimensions_s must contain lesst than IndexMax-1 elements
        Locker l(*const_cast<__self *>(this));
        return index(l);
    }
    return IndexMax;
}
Dimension::Index Dimension::index(Locker &locker) {
    CASppParameterAssert(locker.mutex()==this && locker.owns_lock());
    Dimension::Index i = cachedDimensions_s.size();
    if (++i<IndexMax) {
        locker.unlock();
        boost::upgrade_lock<__self> upgrade(*this);
        do {
            boost::upgrade_to_unique_lock<__self> uniqueLock(upgrade);
            cachedDimensions_s.push_back(*this);
        } while(false);
        locker.lock();
    }
    return i;
}

CASPP_NS_END
